home *** CD-ROM | disk | FTP | other *** search
/ NetNews Offline 2 / NetNews Offline Volume 2.iso / news / comp / lang / c++-part1 / 5250 < prev    next >
Encoding:
Text File  |  1996-08-06  |  6.0 KB  |  226 lines

  1. Path: magnus.acs.ohio-state.edu!csn!ub!newserve!rebecca!rpi!not-for-mail
  2. From: hemal@maverick.corus.co.in (hemal pandya)
  3. Newsgroups: comp.lang.c++,comp.lang.c++.moderated
  4. Subject: Re: Pointer Typecasts
  5. Date: 2 Feb 1996 12:21:58 -0000
  6. Organization: ConnectSoft Ruksun Software India
  7. Sender: cppmods@netlab.cs.rpi.edu
  8. Approved: kanze@lts.sel.alcatel.de
  9. Message-ID: <4esvl6$imh@netlab.cs.rpi.edu>
  10. References: <4ejnr9$cpf@netlab.cs.rpi.edu>
  11. Reply-To: hemal@maverick.corus.co.in
  12. NNTP-Posting-Host: netlab.cs.rpi.edu
  13. X-Original-Date: Fri, 02 Feb 1996 04:03:57 GMT
  14.  
  15. In article <4ejnr9$cpf@netlab.cs.rpi.edu> reckdahl@leland.Stanford.EDU
  16. (Keith Reckdahl) wrote:
  17.  
  18. [details about problems with downcasting from virtual base
  19. class/multiply inherited base class]
  20.  
  21. >What are we doing wrong?
  22.  
  23. struct A {} ;
  24. struct B1 : A {} ;
  25. struct B2 : A {} ;
  26.  
  27. struct D : B1, B2 {} ;
  28.  
  29. Foo(A* a)
  30. {
  31.     D* d2 = (D*) a ;
  32. }
  33.  
  34. In article <4ejnr9$cpf@netlab.cs.rpi.edu> reckdahl@leland.Stanford.EDU
  35. (Keith Reckdahl) wrote:
  36.  
  37. [problem about downcasting from virtual base class/multiply inherited
  38. base
  39. class]
  40.  
  41. >What are we doing wrong?
  42.  
  43. You are making mistake in both cases -- whether you use the virtual
  44. keyword
  45. or not. I will try to explain both of them separately.
  46.  
  47. Lets us say your most base class is called A, classes B1 and B2 are
  48. derived
  49. from A and D is multiply derived from B1 and B2.
  50.  
  51. class A { .... } ;
  52. class B2 : public A { .... } ;
  53. class B2 : public A { .... } ;
  54. class D : public B1, public B2 { .... } ;
  55.  
  56. If you don't use the virtual inheritance, then, as you realize, you
  57. have two
  58. instances of class A for every object of type D. The layout of a D
  59. object
  60. might look like:
  61.  
  62.      +----------------------+
  63.      |  A-part of B1        |
  64.      |                      |
  65.      |    B1 -part          |
  66.      |----------------------|
  67.      |  A-part of B2        |
  68.      |                      |
  69.      |     B2-part          |
  70.      |----------------------|
  71.      |    D -part           |
  72.      |                      |
  73.      |                      |
  74.      +----------------------+
  75.        
  76.  
  77. One thing you should notice is that not all sub-objects of D can start
  78. at
  79. the same address, obviously :) Thus, when a pointer to D is converted
  80. to a
  81. pointer to B2, the bit-pattern of the two pointers will be different:
  82.  
  83. D* d = new D ;
  84. B2* b2 = d ;
  85.  
  86. long(d) != long(b2) ;  // bit patterns of d and b2 are different
  87.  
  88. This can be done (by the compiler) by "adding" to d the offset of the
  89. B2
  90. subobject in a D object.
  91.  
  92. Conversely, to convert from a B2* to D* also, some arithmatic
  93. manipulation
  94. has to be performed. In the above case, the same offset will have to
  95. be
  96. subtracted from the B2* value. How is this done? Typically, the vtable
  97. contains the 'offset to the containing object' field. So given a A*
  98. (inside
  99. a B1) the D* can be calculated. For such a contained object, there is
  100. only
  101. one containing obhect, and a hierearchy of containment can be
  102. traversed to
  103. reach the desired object.
  104.  
  105. Coming back to your original problem: converting a D* to an A* is
  106. ambiguous;
  107. there are two A's in a D. If a D* has to be converted to the A
  108. subobject of
  109. B1 then (according to this scheme) no arithmatic addition/subtraction
  110. needs
  111. to be done while if the case is meant to aquire a pointer to the A
  112. subobject
  113. of B2 the the addition has to be performed. Likewise, the same problem
  114. exists in converting a A* to a D* since it is not known which A the
  115. pointer
  116. points to.
  117.  
  118. Putting in the intermediate base classes gets rid of the compiler
  119. error:
  120.  
  121. A* a = (B1*) d ; // or (B2*) d;
  122.  
  123. eliminates the amiguosity. SInce there is only one B1 in D, a cast to
  124. B1* is
  125. unambiguous and so is the cast from B1* to A*.
  126.  
  127. Going in the other direction,
  128.  
  129. D* d = (D*)(B1*) a ; // ot (D*)(B2*)a ;
  130.  
  131. is unambiguous. But now we reach the catch: given an A* we have to
  132. know
  133. whther its the A part of B1 or B2. If this can be definitely known,
  134. then
  135. this the solution to your problem.
  136.  
  137.  
  138. But maybe this is not right. Im most simple cases, one is looking for
  139. a
  140. virtual inheritance in such cases:
  141.  
  142. class A { .... } ;
  143. class B2 : public virtual  A { .... } ;
  144. class B2 : public virtual A { .... } ;
  145. class D : public B1, public B2, public virtual A { .... } ;
  146.  
  147.   The layout might be:
  148.  
  149.      +----------------------+
  150.      |                      |
  151.      |    B1 -part          |
  152.      |----------------------|
  153.      |                      |
  154.      |     B2-part          |
  155.      |----------------------|
  156.      |    D -part           |
  157.      |                      |
  158.      |----------------------|
  159.      |     A-part           |
  160.      +----------------------+
  161.  
  162.  
  163. Now there is only one A object in every D, casting from D* to A* is
  164. unambigous.
  165.  
  166. A* a = d ; // conversion supllied by the compiler
  167.  
  168. But downcasting is not so simple anymore. Essentially: the virtual
  169. base
  170. class object is "contained" , so to say, by all of B1, B2 and D. The
  171. offset
  172. to the containing object does not make sense in this case.
  173.  
  174. Are you stuck? No. If your compiler supports RTTI, the following will
  175. convert from A* to D*:
  176.  
  177. D* d = dynamic_cast<D*> a ;
  178.  
  179. If the compiler does not support RTTI then you have to take longer
  180. route. The solution is as follows: define a virtual function in the
  181. base
  182. class which returns "this" as a void* and override it in every derived
  183. class. Then, use this function and a cast to do downcasting:
  184.  
  185. class A
  186. {
  187.     virtual void* Self { return this ; }
  188. } ;
  189.  
  190. class B1 : public virtual A
  191. {
  192.     virtual void* Self { return this ; }
  193. } ;
  194.  
  195. class B2 : public virtual A
  196. {
  197.     virtual void* Self { return this ; }
  198. } ;
  199.  
  200. class D : public B1, public B2, virtual public A
  201. {
  202.     virtual void* Self { return this ; }
  203. } ;
  204.  
  205. A* a = d ; 
  206. d = (D*) a->Self() ;
  207.  
  208. This is quite ugly, but it works. Note that following does not work:
  209.  
  210. D* d = (D*)(void*) a ;
  211.  
  212. I leave it you to figure why the Self gimmick works.
  213.  
  214. Hope this helps.
  215.  
  216. >Keith Reckdahl, Paul Mitiguy, Margaret Becker
  217. >reckdahl@leland.stanford.edu
  218.  
  219. - -Hemal
  220.  
  221.  
  222.       [ Articles to moderate: mailto:c++-submit@netlab.cs.rpi.edu ]
  223.       [  Read the C++ FAQ: http://www.connobj.com/cpp/cppfaq.htm  ]
  224.       [  Moderation policy: http://www.connobj.com/cpp/guide.htm  ]
  225.       [      Comments? mailto:c++-request@netlab.cs.rpi.edu       ]
  226.